/*globals Groupon: true, $: false, _: false, Mustache: false, Cookie: false, Finch: false, I18n: false, Countdown: false, FB: false, Control: false, PaginatedAllDeals: false, addAnnouncement: false */

// Generally, this file deals with overcoming stale CDN-cached pages:
// - Sets and gets cookie
// - Defines Groupon.onCurrentUserReady
// - Defines Groupon.onFacebookReady
// - Sets page state with body classes
// - If on deal#show, updates stale deal with a personalized deal
// - Sets Groupon.currentDivision
// - Redirects mobile users
// - Redirects unsubscribed users to subscription flow
// - Checks for SERP referrals
// - Redirects logged in users on a checkout page
// - Ajax call to the personalize controller
// - Updates header area with rendered HTML
// - Updates navigation with personalized links
// - Updates Featured deal with a personalized deal
// - Checks for cachedCheckout Finch experiment
// - Updates buy button based on personalized deal (multioption or not)
// - Renders the deal location map
// - Updates personas with deal types and sets up some persona UI behaviors
// - Update DOM with some persona info/alerts
// - Set up tracking for personas interactions
// - Update places list with currentUser places
// - Check for playback of personalization intro, set cookie otherwise
// - Updates personas places and call to action (??)
// - Update page with any channel-specific branding
// - Update integrated side deals with personalized deals
// - Update Select Area modal
// - Update SEO modal
// - Show marketing drive message if applicable
// - Show deal tags if TUD is not on
// - Show VIP branding if currentUser is VIP
// - Set Finch context for Relevance
// - Build Groupon.currentUser
// - Defines renderContentCallbacks for each type of page (All Deals, Channel Gallery, VIP Gallery, etc.)

Groupon.Cookie = {
  set: function(name, value, days) {
    this.setWithoutEncoding(name, encodeURIComponent(value), days);
  },
  setWithoutEncoding: function(name, value, days) {
    var expires;
    if (days) {
      var date = new Date();
      date.setTime(date.getTime()+(days*24*60*60*1000));
      expires = "; expires="+date.toGMTString();
    } else {
      expires = "";
    }
    document.cookie = name+"="+value+expires+"; path=/";
  },
  get: function(name) {
    var nonDecodedValue = this.getWithoutDecoding(name);
    return nonDecodedValue === null ? null : decodeURIComponent(nonDecodedValue);
  },
  getWithoutDecoding: function(name) {
    var nameEQ = name + "=";
    var ca = document.cookie.split(';');
    for(var i=0;i < ca.length;i++) {
      var c = ca[i];
      while (c.charAt(0)==' ') c = c.substring(1,c.length);
      if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length,c.length);
    }
    return null;
  }
};

Groupon.onCurrentUserReady = function(callback){
  $(function() {
    if (typeof Groupon.currentUser != "undefined" ) {
      return callback();
    }
    $(document).bind('currentUserReady', function(){
      return callback();
    });
  });
};
Groupon.onFacebookReady = function(callback){
  if (typeof Groupon.facebookReady != "undefined" ) {
    return callback();
  }
  $(document).bind('facebookReady', function(){
    return callback();
  });
};

Groupon.Personalization = {
  maybeLoggedInUser :  document.cookie.indexOf('user_id') >= 0,
  maybeSubscriber :  document.cookie.indexOf('subscriber_email') >= 0,
  maybeTracked : location.search !== "",
  resetHiddenContent : function(){
    $('body').removeClass('while_personalizing maybe_logged_in maybe_subscriber divisionless_deal');
    if (!Groupon.thumbsOn) {
      this.revealDealTags();
    }
  },
  addBodyClassName : function(window) {
    var document = window.document;
    var classes = ['while_personalizing'];
    if (!document.body) { return false; }
    if (Groupon.onDealPage) { classes.push("deal_page"); }
    if (Groupon.onDivisionPage) { classes.push("division_page"); }
    if (this.maybeLoggedInUser) { classes.push("maybe_logged_in"); }
    if (this.maybeSubscriber) { classes.push("maybe_subscriber"); }
    if (Groupon.currentDeal && Groupon.currentDeal.divisionless) { classes.push("divisionless_deal"); }
    document.body.className += " " + classes.join(" ");
  },
  getUrlVars : function() {
    var vars = {}, hash;
    var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
    for(var i = 0; i < hashes.length; i++)
    {
      hash = hashes[i].split('=');
      vars[hash[0]] = hash[1];
    }
    return vars;
  },
  updateCurrentDeal : function(data) {
    if( (Groupon.onDealPage || Groupon.onDivisionPage) && Groupon.currentDeal ) {
      if( data.extended_deadline ) {
        Groupon.currentDeal.personalized_deadline_at = data.extended_deadline;
      }
      Groupon.currentDeal.purchasable = (typeof data.purchasable === 'undefined') ? Groupon.currentDeal.purchasable : data.purchasable;
    }
  },
  resetDivision : function(divisionRecord){
    Groupon.currentDivision = divisionRecord.permalink;
    Groupon.Application.setDivision(divisionRecord.permalink);
    Groupon.Analytics.Google.setDivisionCustomVar(divisionRecord.permalink);
    // reset follow drawer selected division/area
    $("#subscription_division_id").val(divisionRecord.permalink);
    if (divisionRecord.area) {
      $("#subscription_division_id").val(divisionRecord.permalink + ":" + divisionRecord.area);
    }
    // reset header links
    $("#logo a").attr('href', divisionRecord.root_path);
    $("#header_nav a.todays_deal").attr('href', divisionRecord.root_path);
    $("#header_nav a.shop_all_deals").attr('href', divisionRecord.all_deals_path);
    $("#header_nav a.getaways").attr('href', divisionRecord.getaways_path);

    // reset footer links
    $(".follow_us a.twitter").attr('href', divisionRecord.twitter_url);
    $(".follow_us a.facebook").attr('href', divisionRecord.fb_url);
    $(".follow_us a.rss").attr('href', divisionRecord.rss_url);

    // reset division drawer
    $("#new_drawer_anchor a").text(divisionRecord.name);
    if (divisionRecord.name.length >= 13) {$("#new_drawer_anchor").addClass("smaller");}
    else {$("#new_drawer_anchor").removeClass("smaller");}
    $('#state_chooser li.selected').removeClass('selected');
    $('#division_list_new ul.current').removeClass('current');
    if (divisionRecord.country == "Canada") {
      $('#state_chooser #state_Canada').addClass('selected');
      $('#division_list_new ul.canada').addClass('current');
    }
    else {
      $('#state_chooser #state_'+divisionRecord.state[0]).addClass('selected');
      $('#division_list_new .state_' + divisionRecord.state[0]).addClass('current');
    }
    $('#division_list_new a.selected').removeClass('selected');
    $('#division_list_new a.E-drawer_click_' + divisionRecord.permalink).addClass('selected');
    // reset subscribe form hidden value
    $(".subscribe_form .subscription_division_id_input").val(divisionRecord.permalink);
    if (divisionRecord.area) {
      $(".subscribe_form .subscription_division_id_input").val(divisionRecord.permalink + ":" + divisionRecord.area);
    }
  },

  handleTouch: function(window) {
    Groupon.isTouchDevice = /webos|android|iphone|blackberry 9800/i.test(window.navigator.userAgent);
    if (Groupon.isTouchDevice && (Groupon.onDealPage || Groupon.onDivisionPage)) {
      if (window.location.href.indexOf('://www.') >= 0){
        Groupon.touchSiteUrl = window.location.href.replace('://www.','://touch.');
      }else {
        Groupon.touchSiteUrl = window.location.href.replace('://','://touch.');
      }
      if (window.location.search.indexOf('mobile=') >= 0) {
        Groupon.touchSiteUrl = Groupon.touchSiteUrl.replace('?mobile=false','?mobile=true');
        Groupon.touchSiteUrl = Groupon.touchSiteUrl.replace('&mobile=false','&mobile=true');
      }else {
        Groupon.touchSiteUrl += (Groupon.touchSiteUrl.indexOf('?') >= 0 ? "&" : "?") + 'mobile=true';
      }
      if (Groupon.Cookie.get('mobile') != 'false' && window.location.search.indexOf('mobile=false') < 0) {
        window.location.replace(Groupon.touchSiteUrl);
      }
      $(document).ready(function(){
        $('#footer').after($('<div id="touch_footer"><a>Go to mobile site</a></div>'));
        $('#touch_footer a').attr('href', Groupon.touchSiteUrl);
      });
    }
  },
  handleSubscriptionTrap: function(window) {
    if (Groupon.cdnCacheable && Groupon.onDivisionPage && Groupon.currentDivision) {
      if (! Groupon.Cookie.get('visited') ){
        Groupon.Cookie.set('visited','visit_1',365);
      } else if (Groupon.Cookie.get('visited') == 'visit_2'){
        Groupon.Cookie.set('visited','true',365);
      } else if (Groupon.Cookie.get('visited') == 'visit_1'){
        Groupon.Cookie.set('visited','visit_2',365);
      }
      if (!Groupon.Cookie.get('user_id') && Groupon.Cookie.get('visited') != "true" && Groupon.fromExternalReferrer && !Groupon.isSerpReferrer && !window.location.href.match("z=dealpage")) {

        window.location.replace("/subscriptions/new?division_p=" + Groupon.currentDivision + window.location.search.replace('?','&'));
      }
    }
  },
  handleReferrer: function(window) {
    var referrer = window.document.referrer.split("?")[0];
    if (referrer.match(/\.groupon\.com/)) {
      Groupon.fromExternalReferrer = false;
      Groupon.isSerpReferrer = false;
    } else {
      Groupon.fromExternalReferrer = true;
      Groupon.isSerpReferrer = this.checkForSerpReferrers(referrer);
    }
  },
  handleCheckoutRedirect: function(deal) {
    var cdnParamIndex, queryString, replacement;
    if (Groupon.onCheckoutPage) {
      if (this.maybeLoggedInUser || (Groupon.currentUser && Groupon.currentUser.isAuthenticated)) {
        // redirect possibly logged in users away from CDN cached page
        queryString = window.location.search;
        cdnParamIndex = queryString.indexOf('anon=true');
        if (cdnParamIndex >= 0) {
          replacement = queryString[cdnParamIndex - 1] === '?' ? 'anon=true&' : '&anon=true';
          window.location.search = queryString.replace(replacement, '');
        }
      } else if (deal && (deal.is_sold_out || deal.is_test_campaign)) {
        if (window.document.referrer) {
          window.location.replace(window.referrer);
        } else if (Groupon.currentDivision) {
          window.location.replace("/" + Groupon.currentDivision);
        } else {
          window.location.replace("/");
        }
      }
    }
  },
  checkForSerpReferrers: function(referrer) {
    if (Groupon.serpReferrers) {
      for(var i=0;i<Groupon.serpReferrers.length;i++){
        var serpReferrer = Groupon.serpReferrers[i];
        if(referrer.match(new RegExp(serpReferrer))) {
          return true;
        }
      }
    }
    return false;
  },
  handleDivisionlessDeals: function() {
    if (Groupon.onDealPage && Groupon.currentDeal && Groupon.currentDeal.divisionless) {
      // reset division; avoid setting wrong division cookie on cached wow deal page
      Groupon.currentDivision = null;
      Groupon.currentArea = null;
    }
  },

  init : function(options){
    if(!options){
      options = {};
    }

    if (Groupon.onDealPage && !Groupon.currentDeal) {
      // do nothing on busted page
      return false;
    }

    // window wrapper
    this.locale = window.location;

    this.personalizeUrl = options.url || '/personalize';

    //doing this to allow external scripts to monkey patch this method in
    if(typeof this.renderContentCallback === 'undefined')
      this.renderContentCallback = options.renderContentCallback;

    if (typeof options.failure === 'function') {
      this.failureCallback = options.failure;
    }

    // Add any existing QS params
    if (this.maybeTracked) {
      this.personalizeUrl += location.search;
    }

    // Deal page only QS params
    if (Groupon.currentDeal && Groupon.currentDeal.permalink && Groupon.onDealPage) {
      this.personalizeUrl += (this.personalizeUrl.indexOf('?') >= 0 ? "&" : "?") + "campaign=" + Groupon.currentDeal.permalink;
      if (Groupon.currentDeal.divisionless) {
        this.personalizeUrl += "&divisionless_deal=true";
      }
    }
    if (Groupon.fromExternalReferrer) {
      var fer = window.document.referrer === "" ? "1" : window.document.referrer;
      this.personalizeUrl += (this.personalizeUrl.indexOf('?') >= 0 ? "&" : "?") + "fer=" + encodeURIComponent(fer);
    }

    this.ajaxCall();
  },
  updateHeaderMenu: function(data) {
    if (data.header_account_menu_html) {
      $('.cdn_cached .account_links').replaceWith(data.header_account_menu_html);
      $('.cdn_cached #header .links').addClass('personalized');
      if (data.connected_with_facebook) {
        Groupon.onFacebookReady(function(){
          FB.XFBML.parse($('.user_avatar')[0]);
        });
      }
    }
  },
  updateNavigation: function(data) {
    if (data.is_authenticated) {
      $('.HowWorks').hide();
    }
    if (data.show_rewards_tab) {
      $('.Rewards').show();
      $('.HowWorks').hide();
    }
    else {
        $('.Rewards').remove();
    }
  },
  updateFeaturedDeal: function(data) {
    if (data.featured_deal && data.featured_deal.html) {
      $('.cdn_cached #content .hproduct').replaceWith(data.featured_deal.html);
      $('.cdn_cached #content .hproduct').addClass('personalized');

      if (Groupon.currentDeal) {
        Groupon.currentDeal.permalink = data.featured_deal.permalink;
        Groupon.currentDeal.title = data.featured_deal.title;
      }

      if (Groupon.Attributes) {
        Groupon.Attributes.share_links = $('#share_links').data('value');
      }
      Groupon.onFacebookReady(function(){
        // regenerate fb like button (groupon says)
        FB.XFBML.parse($('.groupon_share .facebook')[0]);
      });
    }
    this._initMap();
  },

  handleCachedCheckout: function(user, deal) {
    var _this = this,
        count = 0,
        cachedCheckoutInterval;

    // use setInterval to wait until the finch.js experiment has fired or 2s passes
    cachedCheckoutInterval = setInterval(function() {

      // kill it if we're still waiting after 2 seconds
      if (count > 20) {
        clearInterval(cachedCheckoutInterval);
        Groupon.Personalization.cachedCheckout = 'error';
        return false;
      }

      // The cachedCheckout property is set in a finch.js experiment. In some
      // cases, the experiment doesn't run until after the personalization call.
      if (typeof Groupon.Personalization.cachedCheckout === 'undefined') {
        count += 1;
        return false;
      }

      // get rid of the timer once the experiment fires
      clearInterval(cachedCheckoutInterval);

      if (_this.eligableForCachedCheckout(user, deal)) {
        _this.modifyBuyButtonForCachedCheckout();
      }
    }, 100);

    return cachedCheckoutInterval;
  },

  eligableForCachedCheckout: function(user, deal) {
    if (!Groupon.Personalization.cachedCheckout) {
      return false;
    }

    if (user && !user.marketingDriveAvailable && !user.isAuthenticated &&
        deal && !deal.inviteOnly && !Groupon.Dale) {
      return true;
    } else {
      return false;
    }
  },

  // This method is being called from a Finch.js experiment. Pending the results
  // of that experiment, this will begin being called from within personalize.js
  modifyBuyButtonForCachedCheckout: function() {
    var buyButton = $('.deal .primary a.buy_btn');

    if (buyButton.hasClass('multideal')) {
      $('.pledges a[href*="confirmation"]').each(function(i, el) {
        var pledgeButton = $(this);
        pledgeButton.attr('href', pledgeButton.attr('href') + "&anon=true");
      });
    } else {
      buyButton.attr('href', buyButton.attr('href') + "&anon=true");
    }
  },

  disableBuyButtonForDealBank: function() {
    if (Groupon.currentDeal && Groupon.currentDeal.purchasable === false){
      // This work mirrors what's done in deal_showing.js#unpurchasableState
      // eventually we should resolve this duplication
      $('#price_tag .buy_btn').hide();
      $('.button_style_experiment').hide();

      $('#price_tag .unavailable_deal_message').show();
      $('.not_purchasable').fadeIn();
    }
  },

  _initMap: function() {
    var mapSelector = $('#deal_locations_container .gmap_static');
    if(mapSelector.length)
      new Groupon.ui.GrouponMap.Map(mapSelector.get(0), {
        'clickForDynamic': true,
        'markerEvents': {
          'mouseenter': function() {},
          'mouseleave': function() {},
          'click': function(){
            var bubble = $('#map_info_bubble');
            $('body').append(bubble);
            bubble.css({'top': this.positionY(), 'left': this.positionX()});
            bubble.find('h3').html($(this.boundTo[0]).find('.address h4').html());
            bubble.find('.content').html($(this.boundTo[0]).find('.address p').html());
            bubble.toggle();
          }
        }
      });
  },

  updatePersonas: function(data) {
    if(Groupon.thumbsOn) {return;} //no personas w/TUD
    var dealsHavePersonas = $('.deal_tags .persona').length !== 0;
    if (dealsHavePersonas) {
      var personas = ((Groupon.currentUser && Groupon.currentUser.personas) || (data && data.personas));
      if(personas) {
        // Highlight user's active personas
        var activeUserPersonas = _.select(personas, function(p) {return p.status == 'active';}).map(function(p) {return p.persona_id;});
        $(_(activeUserPersonas).map(function(id) { return "[data-persona-id='" + id + "']"; }).join(',')).addClass('matching');

        // Add record id attribute to all user profile personas (active and deleted)
        _.each(personas, function(p) { $("[data-persona-id='" + p.persona_id + "']").data('user-profile-persona-id', p.id); });

        if (Groupon.onAllDealsPage || Groupon.onVIPGalleryPage ) {
          // Success message & highlight matching tiles
          var persona_matches = $(".featured, .one_up, .three_up").has('.matching');
          persona_matches.find('.all_deals_match').addClass('matching');

          if( Groupon.onAllDealsPage ) {
            var persona_matches_count = persona_matches.length;
            // Remove the success container first, because it might already be on the page when loading paginated deals
            $("#tag_success_container").remove();
            if (persona_matches_count > 0){
              $("<div id='tag_success_container'></div>").html(
                Mustache.to_html(Groupon.MustacheTemplates['groupon/deals/all_deals/_tag_match'], {number: persona_matches_count, is_plural: persona_matches_count !== 1})
              ).prependTo("#deal_container");
            }
          }
        }
      }

      // Make deal tags clickable
      $('#main .deal_tags .persona').PersonaToggler();

      // Handle query string to add persona
      var params = new RegExp("[\\?&]psid=([^&#]*)").exec(this.locale.search);
      var psid = params !== null ? params[1] : "";
      if(psid.length > 0) {
        if(!data.profile) {
          // Login required for anonymous users
          this.locale.href = "/login";
        }
        else {
          // Add/activate persona
          var tagEl = $(".deal_tags .persona[data-persona-id='" + psid + "']");
          var tag = tagEl.data('PersonaToggler');
          if(tag !== undefined) {
            tagEl.data('animate', false);   // flag to suppress animation
            tag.add();
          }
        }
      }

      if(Groupon.onAllDealsPage) { this.personasFeedback(); }
      this.personasTracking();

    }
  },


  personasFeedback: function() {
    var notificationBubbleEl = $('#deal_tag_notification');
    var notificationBubble = new Groupon.ThoughtBubble(notificationBubbleEl, {animateTransitions: false});
    $('#main .deal_tags .persona').bind('personaAdded', function() {
      $('#header_nav .personas_badge').show();

      if($(this).data('animate') !== false) {
        notificationBubble.flashMessage('<em>Got it!</em> <a href="/' + Groupon.currentDivision + '/all" onclick="javascript:Groupon.Personas.Tracker.get().trackAllDealsRefreshDeals();">Refresh</a> to see what deals match your Deal Types.');

        Groupon.Personas.FlyingHeart.animate(this);
      }
      else {
        $(this).data('animate', true);
      }
    });

    // Float notification bubble when scrolling
    notificationBubbleEl.StickyScroll({
      position: 'top',
      targetY: parseInt($('#header_nav').offset().top, 10) + 38
    });
  },

  personasTracking: function() {
    // Pixel tracking
    if(Groupon.Personas.Tracker) {
      var tracker = Groupon.Personas.Tracker.get();
      var personaSelector = '#main .deal_tags .persona';

      if (Groupon.onAllDealsPage || Groupon.onChannelGalleryPage) {
        tracker.trackAllDealsView(
          $(personaSelector).length > 0,
          $(personaSelector + '.matching').length > 0);
        $(personaSelector).bind('personaAdded', function() {
          tracker.trackAllDealsAddPersona(
            $(this).data('permalink'),
            $(this).parents('.hoverable').data('permalink'));
        });
        $(personaSelector).bind('personaRemoved', function() {
          tracker.trackAllDealsRemovePersona(
            $(this).data('permalink'),
            $(this).parents('.hoverable').data('permalink'));
        });
      } else if (Groupon.onDealPage || Groupon.onDivisionPage) {
        $(personaSelector).bind('personaAdded', function() {
          tracker.trackDealPageAddPersona($(this).data('permalink'), Groupon.currentDeal.permalink);
          });
        $(personaSelector).bind('personaRemoved', function() {
          tracker.trackDealPageRemovePersona($(this).data('permalink'), Groupon.currentDeal.permalink);
        });
      }
    }
  },


  updatePlaces: function(data) {
    if(data.closest_place) {
      var description;
      var closest = data.closest_place;
      $('.places_container').append(Mustache.to_html(Groupon.MustacheTemplates['groupon/deals/_places_tag'], {
        name: closest.name,
        description: closest.description,
        distance_class: closest.distance > 5 ? 'more_than_five' : 'less_than_five'
      }));
    }
    else if(Groupon.currentDeal.location_format == 'no_locations') {
      $('.places_container').append(Mustache.to_html(Groupon.MustacheTemplates['groupon/deals/_places_tag'], {
        name: 'no_loc',
        description: Groupon.currentDeal.redemption_location,
        distance_class: ''
      }));
    }
  },

  updatePreferences: function(profile) {
    if (profile) {
      // Capture that user has dismissed the intro
      $.ajax({
        url: '/profile/preferences',
        type: 'POST',
        timeout: 10000,
        data: {'show_personalization_intro': 'false', '_method': 'PUT'}
      });
    } else {
      Cookie.set('show_personalization_intro', 'false');
    }
  },

  updatePersonasPlacesCallToAction: function(data) {
    if(Groupon.thumbsOn) {return;} //no personas w/TUD
    var _this = this;
    var dealHasTags = $('.deal_tags .persona').length !== 0;
    var userHasTags = data.personas && data.personas.length !== 0;
    var userHasPlaces = data.places && data.places.length !== 0;

    if(dealHasTags) {
      // One-time intro to personalization
      if(data.show_personalization_intro && !userHasTags) {
        $('.personalization_bar_container').append(Mustache.to_html(Groupon.MustacheTemplates['common/shared/_thought_bubble'], {thought_bubble_id: 'personalization_intro'}));
        Groupon.PersonalizationIntro = new Groupon.RandomThoughtBubble($('#personalization_intro'), {
          closeCallback: function() {
            _this.updatePreferences(data.profile);
          }
        });
        $('.deal_tags .persona').bind('personaAdded', function() {
          Groupon.PersonalizationIntro.outro();
        });
      }
      // Tagging feedback
      var notificationBubbleEl = $('#deal_tag_notification');
      var notificationBubble = new Groupon.ThoughtBubble(notificationBubbleEl, {animateTransitions: false});
      $('.deal_tags .persona').bind('personaAdded', function() {
        $('#header_nav .personas_badge').show();
        if($(this).data('animate') !== false) {
          notificationBubble.flashMessage('<em>Got it!</em> <a href="/' + Groupon.currentDivision + '/all" onclick="javascript:Groupon.Personas.Tracker.get().trackDealPageRefreshDeals();">Check for more</a> deals that match ' + $(this).data('name') + '.');
          Groupon.Personas.FlyingHeart.animate(this);
        }
        else {
          $(this).data('animate', true);
        }
      });

      notificationBubbleEl.StickyScroll({
        position: 'top',
        targetY: parseInt($('#header_nav').offset().top, 10) + 38
      });

      // Deal Tags persistent call-to-action
      if(!userHasTags && !data.show_personalization_intro) {
        $('.tag_container').before(Mustache.to_html(Groupon.MustacheTemplates['groupon/deals/_deal_tags_cta']));
      }

      // Places persistent call-to-action
      if(Groupon.currentDeal.location_format == 'locations') {  // deal has geocodable locations
        var ctaText = "";
        if(!userHasPlaces) {
          ctaText = "Add Home";
        }
        else if(data.closest_place && data.closest_place.distance && data.closest_place.distance > 5) {
          var userHasHome = _.any(data.places, function(p) {return p.name == 'home';});
          var userHasWork = _.any(data.places, function(p) {return p.name == 'work';});
          if(!userHasHome) {
            ctaText = "Add Home";
          }
          else if(!userHasWork) {
            ctaText = "Add Work";
          }
        }
        if(ctaText.length > 0) {
          $('.places_container').append(Mustache.to_html(Groupon.MustacheTemplates['groupon/deals/_places_cta'], {
            has_places: !!userHasPlaces,
            places_url: '/places/new',
            prompt: ctaText
          }));
        }
      }
    }
  },

  updateChannelBranding: function(data) {
    if(data.show_channel && Groupon.currentChannel){
      $('body').addClass(Groupon.currentChannel.permalink);
    }
  },

  updateSideDeals: function(data) {
    if (data.side_deal_permalinks) {
      if (Groupon.integratedSideDealsRedesign && data.side_deals_data) {
        $('.cdn_cached #rail .integrated_side_deals').html(Mustache.to_html(
          Groupon.MustacheTemplates['groupon/widgets/integrated_side_deals'],
          { data_source: 'integrated_side_deals',
            title: "More Great Deals",
            see_all_path: '/all',
            side_deals: data.side_deals_data},
          {side_rail_deal: Groupon.MustacheTemplates['groupon/widgets/_side_rail_deal']})
        ).addClass('side_deals_redesign personalized');
        $('.cdn_cached #rail .integrated_side_deals').trigger('updated');
      }
      else if(data.side_deals_html){
        $('.cdn_cached #rail .integrated_side_deals').html(data.side_deals_html).addClass('personalized');
        $('.cdn_cached #rail .integrated_side_deals').trigger('updated');
      }
      Groupon.sideDeals = data.side_deal_permalinks;
    }
  },

  updateAreaSelectModal: function(data) {
    if (data.area_select_modal_html) {
      $('<div id="area_select_modal"></div>').appendTo($('body'));
      $('#area_select_modal').html(data.area_select_modal_html);
      if (Groupon.Attributes) {
        Groupon.Attributes.primary_area = $('#primary_area').data('value');
      }
    }
  },
  updateSeoModal: function(data) {
    if (data.seo_modal_html) {
      $(data.seo_modal_html).appendTo($('body'));
      Control.Modal.open($("#seo_subscribe_modal"));
    }
  },
  showMarketingDriveMessage: function(data) {
    //show marketing drive messaging in the announcement slot, unless the invite-only deal announcement is there
    if(data.marketing_drive_message && Groupon.currentDeal && !Groupon.currentDeal.inviteOnly) {
      addAnnouncement(Mustache.to_html(Groupon.MustacheTemplates[data.marketing_drive_message.template], data.marketing_drive_message));

      if (Groupon.Now && Groupon.Now.Tracker) {
        var tracker  = Groupon.Now.Tracker.get();
        tracker.trackDriveAnnouncementView("deal_page");
        $(".TP_get_now_deal").click(function() {
          tracker.trackDriveAnnouncementClick("deal_page");
        });
      }
    }
  },

  redoDealTags: function(data) {
    if (!Groupon.thumbsOn) {
      Groupon.Personalization.updatePersonas(data);
      // Groupon.Personalization.updatePersonasPlacesCallToAction(data); (broken)
      Groupon.Personalization.DealPage.showDealTypes();
      this.revealDealTags();
    }
  },

  revealDealTags: function() {
    $('.deal_tags .thumbs_on').removeClass('thumbs_on');
  },

  // TODO: remove this unneccessary wrapper method
  updateDivisionReferences: function(data) {
    if (data.division_record) {
      this.resetDivision(data.division_record);
    }
  },
  updateVip: function(data) {
    if (data.vip) {
      $("body, #header").addClass("vip");
    }
  },
  setRelevanceContext: function(data) {
    if(typeof Finch.externalData == 'undefined') {
      Finch.externalData = {};
    }
    $.extend(Finch.externalData, {
      rs_context: data.relevance_context,
      rs_treatment: data.relevance_treatment
    });
  },
  buildCurrentUser: function(data) {
    var currentUser = {};
    currentUser.showSaveLocation            = data.show_save_location;
    currentUser.isAuthenticated             = data.is_authenticated;
    currentUser.flash                       = data.flash;
    currentUser.subscribedToCurrentDivision = data.subscribed_to_current_division;
    currentUser.subscribedToCurrentChannel  = data.subscribed_to_current_channel;
    currentUser.marketingDriveAvailable     = data.marketing_drive_message ? true : false;
    currentUser.vip                         = data.vip;
    if (data.is_authenticated) {
      currentUser.permalink                 = data.permalink;
      currentUser.emailAddress              = data.email_address;
      currentUser.trackingCode              = data.tracking_code;
      currentUser.connectedWithFacebook     = data.connected_with_facebook;
      currentUser.facebookUid               = data.facebook_uid;
      currentUser.firstName                 = data.first_name;
      // TODO stop setting global state here
      Groupon.Attributes.user               = Groupon.Attributes.user || {};
      Groupon.Attributes.user.tracking_code = currentUser.trackingCode;
      Groupon.Attributes.user.permalink     = currentUser.permalink;
    }
    if (data.profile) {
      currentUser.profile                   = data.profile;
      currentUser.personas                  = data.personas;
      currentUser.places                    = data.places;
      currentUser.marketingDrives           = data.marketing_drives;
    }
    return currentUser;
  },

  // Temp fix to show stale deals on JS failure
  // try/catch changes context, HALP
  trySuccessHandler: function(data) {
    try {
      Groupon.Personalization.successHandler(data);
    } catch (e) {
      var errorData = {
        personalizeUrl: Groupon.Personalization.personalizeUrl,
        ajaxParams: JSON.stringify(Groupon.Personalization.ajaxParams()),
        error: JSON.stringify(e)
      };
      Groupon.Analytics.PixelTracker.injectTrackingPixel('personalize_error', errorData);

      Groupon.Personalization.errorHandler(data);
    }
  },

  successHandler : function(data){
    var _this = this;

    $(document).ready(function(){
      _this.updateCurrentDeal(data);
      _this.handleCheckoutRedirect(data);
      _this.updateHeaderMenu(data);
      _this.updateNavigation(data);
      _this.updateFeaturedDeal(data);
      _this.updateSideDeals(data);
      _this.updateAreaSelectModal(data);
      _this.updateSeoModal(data);
      _this.updateDivisionReferences(data);
      _this.showMarketingDriveMessage(data);
      _this.disableBuyButtonForDealBank();
      _this.updateVip(data);
      _this.setRelevanceContext(data);
      _this.resetHiddenContent();

      // Custom contentCallback -- page specific content rendering.
      //    - As we enable CDN caching on more pages, each will have a unique templates and rendering for the returned deals
      if(_this.renderContentCallback){
        _this.renderContentCallback(data);
      }

      _this.resetHiddenContent();
      Groupon.currentUser = _this.buildCurrentUser(data);

      _this.handleCachedCheckout(Groupon.currentUser, Groupon.currentDeal);

      $.event.trigger('currentUserReady', Groupon.currentUser);
    });
  },
  errorHandler : function(data){
    var _this = this;
    $(document).ready(function(){
      if (_this.failureCallback) {
        _this.failureCallback(data);
      }

      _this.resetHiddenContent();
      Groupon.currentUser = {};
      Groupon.currentUser.isAuthenticated = false;
      Groupon.currentUser.subscribedToCurrentDivision = false;
      Groupon.currentUser.subscribedToCurrentChannel = false;
      $.event.trigger('currentUserReady', Groupon.currentUser);
    });
  },
  ajaxParams: function() {
    var ajaxParams = {};
    if (Groupon.currentChannel) {
      ajaxParams.channel_id = Groupon.currentChannel.id;
    }
    if (typeof I18n !== "undefined" && I18n.locale) {
      ajaxParams.locale = I18n.locale;
    }
    if (this.getUrlVars().pt !== undefined) {
      ajaxParams.pt = this.getUrlVars().pt;
    }
    return ajaxParams;
  },
  ajaxCall: function(){
    var data = $.parseJSON(Groupon.personalizationContent);
    if (data) {
      this.trySuccessHandler(data);
    } else {
      $.ajax({
        url: this.personalizeUrl,
        data: this.ajaxParams(),
        type: "GET",
        dataType: "json",
        timeout: 10000,
        context: this,
        success: this.trySuccessHandler,
        error: this.errorHandler
      });
    }
  }

};

Groupon.Personalization.DealPage = {
  url : '/personalize',

  showDealTypes: function() {
    if(Groupon.thumbsOn) {return;} //no personas w/TUD
    if(($('.deal_tags > *').length > 0 || $('.personalization_bar .places_container > *').length > 0)) {
      $('#personalization_intro').css('bottom', $('.personalization_bar').height()-63);
      $('.personalization_bar').show();
      window.setTimeout(function() {
        if(Groupon.PersonalizationIntro) {
          Groupon.PersonalizationIntro.showRandomMessage();
        }
      }, 2000);
      Groupon.Personas.Tracker.get().trackDealPageView(
        $('.deal_tags .persona').length > 0,
        $('.deal_tags .persona.matching').length > 0);
    }
  },

  updateThumbsAttributes: function(data) {
    if (data.thumbs_attributes) {
      $(".thumbs").replaceWith(
        Mustache.to_html(Groupon.MustacheTemplates['groupon/components/thumbs/_container'], data.thumbs_attributes)
      );
    }
  },

  renderContentCallback : function(response){
    // Feature blocks
    Groupon.vipEnabled = response.vip_enabled;

    Groupon.Personalization.updatePersonas(response);
    Groupon.Personalization.updatePlaces(response);
    Groupon.Personalization.updatePersonasPlacesCallToAction(response);
    Groupon.Personalization.DealPage.showDealTypes();

    Groupon.Personalization.updateChannelBranding(response);

    if (Groupon.thumbsOn) {
      Groupon.Personalization.DealPage.updateThumbsAttributes(response);
    }

    // initialize personalize callout
    if(response.profile && Groupon.PersonalizeCallout) {
      Groupon.PersonalizeCallout.init(response.personas, response.profile.gender, response.places);
    }
  }
};

Groupon.Personalization.Checkout = {
  url: '/personalize/checkout'
};

Groupon.Personalization.ChannelGallery =  {
  renderContentCallback : function(response){
    // Personas behavior
    Groupon.Personalization.updatePersonas( response );
  }
};

Groupon.Personalization.VIPGallery =  {
  renderContentCallback : function(response){
    // Personas behavior
    Groupon.Personalization.updatePersonas( response );
  }
};

Groupon.Personalization.AllDeals = {
  url : '/personalize/all_deals',

  updateVip: function(data) {
    if (data.vip) {
      $('body').addClass('vip');
      $('.page_header h2').hide();

      var shop_all_deals_path = '/all';
      var vip_deals_path = '/vip/deals';
      $("<div class='vip_nav'></div>").html(
        Mustache.to_html(Groupon.MustacheTemplates['groupon/deals/all_deals/_vip_header'], {shop_all_deals_selected: 'selected', shop_all_deals_path: shop_all_deals_path, vip_deals_path: vip_deals_path })
      ).prependTo('.page_header');
    }
  },

  renderContentCallback : function(response){
    // Personas behavior
    $(document).bind('dealsRendered', function() {
      Groupon.Personalization.updatePersonas( response );
    });

    // Add VIP Treatment
    Groupon.Personalization.AllDeals.updateVip(response);

    // initialize personalize callout
    if(response.profile && Groupon.PersonalizeCallout) {
      Groupon.PersonalizeCallout.init(response.personas, response.profile.gender, response.places);
    }

    var dealContainerEl = $('#deal_container');
    var c = 'all';

    if (Groupon.allDealsGrouped) {
      var featuredDeal = response.featured_deal;
      var groups = response.groups;

      var renderDealGroups = function() {
        var paginate_deals = false;
        $(groups).each(function(index, group){
          if(group.deals && group.deals.length !== 0) {
            paginate_deals = paginate_deals || group.show_more_button;
            dealContainerEl.append(Mustache.to_html(Groupon.MustacheTemplates['groupon/deals/all_deals/_deal_group'], group, Groupon.MustacheTemplates));
          }
        });
        if (paginate_deals) PaginatedAllDeals.init(groups, true);
        $(document).trigger('dealsRendered');
      };

      if(featuredDeal && groups && groups.length !== 0){
        dealContainerEl.empty();
        dealContainerEl.append(Mustache.to_html(Groupon.MustacheTemplates['groupon/deals/all_deals/_deal'], featuredDeal, Groupon.MustacheTemplates));
        renderDealGroups();
      }
    } else {
      var deals = response.deals;
      if(deals && deals.length !== 0){
        dealContainerEl.empty();
        $(deals).each(function(index, deal){
          dealContainerEl.append(Mustache.to_html(Groupon.MustacheTemplates['groupon/deals/all_deals/_deal'], deal, Groupon.MustacheTemplates));
        });
        $(document).trigger('dealsRendered');
      }
    }
  }
};


// TODO move some/all of this into the init() function
Groupon.Personalization.handleCheckoutRedirect();
Groupon.Personalization.handleDivisionlessDeals(window);
Groupon.Personalization.handleReferrer(window);
Groupon.Personalization.handleSubscriptionTrap(window);
Groupon.Personalization.handleTouch(window);

if(Groupon.onDealPage || Groupon.onDivisionPage) {
  Groupon.Personalization.init(Groupon.Personalization.DealPage);

} else if(Groupon.onAllDealsPage){
  Groupon.Personalization.init(Groupon.Personalization.AllDeals);

} else if (Groupon.onChannelGalleryPage) {
  Groupon.Personalization.init(Groupon.Personalization.ChannelGallery);

} else if (Groupon.onVIPGalleryPage) {
  Groupon.Personalization.init(Groupon.Personalization.VIPGallery);

} else if (Groupon.onCheckoutPage) {
  Groupon.Personalization.init(Groupon.Personalization.Checkout);

} else {
  Groupon.Personalization.init();
}
